package za.um.td.entity.level;

import java.util.ArrayList;

import com.jme.bounding.BoundingBox;
import com.jme.entity.Entity;
import com.jme.renderer.ColorRGBA;
import com.jme.scene.Geometry;
import com.jme.scene.Line;
import com.jme.scene.Node;
import com.jme.scene.Spatial.LightCombineMode;
import com.jme.scene.Spatial.TextureCombineMode;
import com.jme.scene.state.TextureState;
import com.jme.scene.state.ZBufferState;
import com.jme.scene.state.ZBufferState.TestFunction;
import com.jme.system.DisplaySystem;
import com.jme.math.FastMath;
import com.jme.math.Vector3f;

public class AxisGrid extends Entity{
	
	/**
	 * If drawBehind is false, the grid is tested with the z-buffer, which means that
	 * only the visible parts of the grid will be seen and objects in front of the grid will
	 * hide it.
	 */
	protected boolean drawBehind = false;
 
	/**
	 * If drawAxis is true, Red, Green and Blue axis are drawn for the XYZ axis. Axis are
	 * never rotated, so they are always world axis, but they are translated to the grid
	 * center.
	 */
	protected boolean drawAxis = true;
 
	/**
	 * Center of the grid.
	 */
	protected final Vector3f center = new Vector3f();
 
	/**
	 * Space between grid lines.
	 */
	protected float spacing = 1.0f;
 
	/**
	 * Draw a bold line every given number of lines.
	 */
	protected int marker = 5;
 
	/**
	 * Grid rotation. This is an Euler rotation (X,Y,Z). Values are multiplied by PI
	 * before being applied.
	 */
	protected final Vector3f rotatePi = new Vector3f();
 
	/**
	 * Grid extent. The extent is applied towards the positive and also negative sides of the grid.
	 */
	//protected float extent = 40.0f;
	protected float extent = 1;
	

	public AxisGrid(String id) {
		super(id);
		this.setSpatial(buildGeometry(new Vector3f(1,1,1),new Vector3f(1,1,1),16));
		this.getSpatial().lock();
	}
	
	public AxisGrid(String id, Vector3f axisLength, Vector3f scale, int gridSize) {
		super(id);
		this.setSpatial(buildGeometry(axisLength, scale, gridSize));
		this.getSpatial().lock();
	}
	
	
	private Node buildGeometry(Vector3f axisLength, Vector3f scale, int gridSize) {

		Node grid = new Node("grid");
		Node axis = new Node("axis");
		Node axisGrid = new Node("axisGrid");
 
		//Create Grid
		ArrayList<Vector3f> markerVertices = new ArrayList<Vector3f>();
		ArrayList<Vector3f> regularVertices = new ArrayList<Vector3f>();
		for (int i = 0; i * spacing <= gridSize/2; i++) {
 
			if (i % marker > 0) {
				// Normal line
				
				//lines along the X
				regularVertices.add(new Vector3f(-extent*axisLength.x/2, 0, i * spacing*scale.z));
				regularVertices.add(new Vector3f(extent*axisLength.x/2, 0, i * spacing*scale.z));
				
				regularVertices.add(new Vector3f(-extent*axisLength.x/2, 0, -i * spacing*scale.z));
				regularVertices.add(new Vector3f(extent*axisLength.x/2, 0, -i * spacing*scale.z));
				
				
				//lines along Z
				regularVertices.add(new Vector3f(i * spacing*scale.x, 0, -extent*axisLength.z/2));
				regularVertices.add(new Vector3f(i * spacing*scale.x, 0, extent*axisLength.z/2));
				regularVertices.add(new Vector3f(-i * spacing*scale.x, 0, -extent*axisLength.z/2));
				regularVertices.add(new Vector3f(-i * spacing*scale.x, 0, extent*axisLength.z/2));
			} else {
				// Marker line
				markerVertices.add(new Vector3f(-extent*axisLength.x/2, 0, i * spacing*scale.z));
				markerVertices.add(new Vector3f(extent*axisLength.x/2, 0, i * spacing*scale.z));
				markerVertices.add(new Vector3f(-extent*axisLength.x/2, 0, -i * spacing*scale.z));
				markerVertices.add(new Vector3f(extent*axisLength.x/2, 0, -i * spacing*scale.z));
				if (i != 0) {
					markerVertices.add(new Vector3f(i * spacing*scale.x, 0, -extent*axisLength.z/2));
					markerVertices.add(new Vector3f(i * spacing*scale.x, 0, extent*axisLength.z/2));
					markerVertices.add(new Vector3f(-i * spacing*scale.x, 0, -extent*axisLength.z/2));
					markerVertices.add(new Vector3f(-i * spacing*scale.x, 0, extent*axisLength.z/2));
				}
			}
 
		}
 
		Geometry regularGrid = new Line("regularLine", regularVertices
				.toArray(new Vector3f[] {}), null, null, null);
		regularGrid.getDefaultColor().set(ColorRGBA.darkGray);
		grid.attachChild(regularGrid);
		Geometry markerGrid = new Line("markerLine", markerVertices
				.toArray(new Vector3f[] {}), null, null, null);
		regularGrid.getDefaultColor().set(ColorRGBA.lightGray);
		grid.attachChild(markerGrid);
 
		grid.getLocalRotation().fromAngles(FastMath.PI * rotatePi.x,
				FastMath.PI * rotatePi.y, FastMath.PI * rotatePi.z);
 
		grid.setLocalTranslation(new Vector3f(0.5f, 0.01f, 0.5f));
		axisGrid.attachChild(grid);
 
		// Create Axis
 
		if (drawAxis) {
 
			Vector3f xAxis = new Vector3f(extent*axisLength.x/2, 0, 0); //red
			Vector3f yAxis = new Vector3f(0, extent*axisLength.y/2, 0); //green
			Vector3f zAxis = new Vector3f(0, 0, extent*axisLength.z/2); //blue
 
			ColorRGBA[] red = new ColorRGBA[2];
			red[0] = new ColorRGBA(ColorRGBA.red);
			red[1] = new ColorRGBA(ColorRGBA.red);
 
			ColorRGBA[] green = new ColorRGBA[2];
			green[0] = new ColorRGBA(ColorRGBA.green);
			green[1] = new ColorRGBA(ColorRGBA.green);
 
			ColorRGBA[] blue = new ColorRGBA[2];
			blue[0] = new ColorRGBA(ColorRGBA.blue);
			blue[1] = new ColorRGBA(ColorRGBA.blue);
 
			Line lx = new Line("xAxis", new Vector3f[] { xAxis.negate(), xAxis },
					null, red, null);
			Line ly = new Line("yAxis", new Vector3f[] { yAxis.negate(), yAxis },
					null, green, null);
			Line lz = new Line("zAxis", new Vector3f[] { zAxis.negate(), zAxis },
					null, blue, null);
 
			lx.setModelBound(new BoundingBox()); // Important to set bounds to prevent some error
			lx.updateModelBound();
			ly.setModelBound(new BoundingBox()); // Important to set bounds to prevent some error
			ly.updateModelBound();
			lz.setModelBound(new BoundingBox()); // Important to set bounds to prevent some error
			lz.updateModelBound();
 
			axis.attachChild(lx);
			axis.attachChild(ly);
			axis.attachChild(lz);
 
			axisGrid.attachChild(axis);
 
		}
 
		// RenderStates for the whole grid and axis
 
		TextureState ts = DisplaySystem.getDisplaySystem().getRenderer()
				.createTextureState();
		axisGrid.setTextureCombineMode(TextureCombineMode.Off);
		axisGrid.setRenderState(ts);
 
		ZBufferState zs = DisplaySystem.getDisplaySystem().getRenderer()
				.createZBufferState();
		zs.setFunction(drawBehind ? TestFunction.Always
				: TestFunction.LessThanOrEqualTo);
		zs.setWritable(false);
		zs.setEnabled(true);
		axisGrid.setRenderState(zs);
 
		axisGrid.setLightCombineMode(LightCombineMode.Off);
 
		axisGrid.updateRenderState();
 
		axisGrid.getLocalTranslation().set(center);
		axisGrid.updateGeometricState(0, true);
 
		axisGrid.lock();
 
		return axisGrid;
	}
}
